Follow-up: Batch Import, Thumbnail Render, and Automation in KeyShot
Hi everyone,
During our recent Office Hours webinar, we received a great question about automating a batch process in KeyShot. We ran out of time before we could cover it live, so I wanted to follow up here with both a short video and the actual Python script we walked through.
The question was:
How can I script KeyShot to open a folder of files, import each object, create a thumbnail render, close the file, and repeat the task?
To help with that, I’ve included a script called batch_import_render_queue.py
(see below). It’s designed to:
- Import all CAD files in a selected directory
- Apply a material template (optional)
- Automatically set up the camera
- Queue and optionally process a thumbnail-style render
- Repeat the process for each file
The script uses a simple GUI dialog to walk you through the settings. You can set file type, resolution, material template, and the output folder. Once it runs, KeyShot cycles through each file and handles the whole process without needing manual input between imports.
About the Thumbnail Class
One point I mentioned in the video that’s worth clarifying: If you’ve seen the class lux.ThumbnailRenderOptions
class in the KeyShot Python API, that class is not what we’re using here.
The Thumbnail
class is meant specifically for material thumbnails. It’s for use with the Material Information Manager and in headless workflows. If you’re trying to render scene-level thumbnails (like we’re doing here), you won’t be using the lux.ThumbnailRenderOptions
class at all. Instead, this script just queues a standard renderImage()
call with your chosen resolution.
What the GUI Does
Here’s what happens under the hood:
- You select a directory of files (e.g.
.fbx
,.obj
, etc.) - Each file is imported into a new scene
- An optional material template is applied
- The model set is renamed based on the file name
- A thumbnail image is rendered and saved
- Repeat for each file in the folder
You can also toggle whether to process the queue automatically at the end. It’s flexible enough to support various formats and workflows, especially if you’re prepping batches of models for review or asset libraries.
Note, this is example script is not an Official KeyShot script and therefore does not include any support.
# AUTHOR: LUX DT ChatGPT
# VERSION: 2.0.3
# This KeyShot Python script automates the batch import and rendering of CAD files from a selected directory.
# Designed for efficiency and consistency, it enables users to import models, apply material templates, set up cameras automatically,
# and queue renders—all through a single GUI dialog.
import lux
import os
def clean_ext(ext):
while ext.startswith("."):
ext = ext[1:]
return ext.lower()
def create_dialog():
templates = ["-- None --"] + lux.getMaterialTemplates()
values = [
("import_dir", lux.DIALOG_FOLDER, "Select Import Directory:", None),
("file_ext", lux.DIALOG_TEXT, "Enter file extension (e.g. obj, fbx, igs):", "fbx"),
("material_template", lux.DIALOG_ITEM, "Material Template (optional):", templates[0], templates),
("resolution", lux.DIALOG_TEXT, "Render Resolution (e.g. 256x256):", "256x256"),
("output_dir", lux.DIALOG_FOLDER, "Select Render Output Directory:", None),
("queue_render", lux.DIALOG_CHECK, "Queue Render?", True),
("process_queue", lux.DIALOG_CHECK, "Process Queue After Import?", True),
]
return lux.getInputDialog(
title="Batch Import CAD and Queue Renders",
desc="Import all CAD files, apply materials, set cameras, and queue renders.",
values=values,
id="batch_import_render_dialog_v3"
)
def get_import_options():
return {
"adjust_camera_look_at": True,
"adjust_environment": True,
"center_geometry": True,
"snap_to_ground": True
}
def parse_resolution(res_str):
parts = res_str.lower().split("x")
if len(parts) != 2:
raise ValueError("Invalid resolution format. Use WxH (e.g. 256x256).")
return int(parts[0]), int(parts[1])
def rename_default_model_set(new_name):
root = lux.getSceneTree()
for node in root.getChildren():
if node.isModelSet():
node.setName(new_name)
return
def main():
opts = create_dialog()
if not opts:
return
folder = opts["import_dir"]
ext = clean_ext(opts["file_ext"])
template = opts["material_template"]
width, height = parse_resolution(opts["resolution"])
out_dir = opts["output_dir"]
queue = opts["queue_render"]
process = opts["process_queue"]
files = [f for f in os.listdir(folder) if f.lower().endswith(ext)]
if not files:
lux.messageBox("No files found with extension ." + ext)
return
for file_name in files:
lux.newScene()
file_path = os.path.join(folder, file_name)
lux.importFile(file_path, opts=get_import_options())
# Apply material template if valid
if isinstance(template, list) and len(template) == 2 and template[1] != "-- None --":
lux.setMaterialTemplate(template[1])
# Rename default model set to the file name (without extension)
base_name = os.path.splitext(file_name)[0]
rename_default_model_set(base_name)
# Prepare render options
render_opts = lux.getRenderOptions()
render_opts.setAddToQueue(queue)
output_path = os.path.join(out_dir, base_name + "_render.png")
# Queue render
lux.renderImage(output_path, width=width, height=height, opts=render_opts)
if process:
lux.processRenderQueue()
main()
Let me know if you have any questions.